﻿using LiteNetLib;
using LiteNetLib.Utils;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

namespace BToolkit
{
    public class NetBroadcaster : MonoBehaviour
    {
        private static NetBroadcaster _instance;
        public static NetBroadcaster Instance
        {
            get
            {
                if (!_instance)
                {
                    _instance = FindObjectOfType<NetBroadcaster>();
                    if (!_instance)
                    {
                        GameObject go = new GameObject("NetBroadcaster");
                        DontDestroyOnLoad(go);
                        _instance = go.AddComponent<NetBroadcaster>();
                    }
                }
                return _instance;
            }
        }

        public static event Action<ProtoBase> onReceiveOnSubThread, onReceiveOnMainThread;

        private NetManager receiver;
        private NetManager sender;
        private NetDataWriter writer;
        private int port;
        private bool isRun;
        private Queue<Action> mainThreadActions = new Queue<Action>();

        public void Init(int port)
        {
            this.port = port;

            if (receiver != null && sender != null) return;

            //接收者（不能和发送者共用，因为Start(port)这里要传监听端口）
            EventBasedNetListener receiverListener = new EventBasedNetListener();
            receiverListener.NetworkReceiveUnconnectedEvent += (point, reader, type) =>
            {
                if (type == UnconnectedMessageType.Broadcast)
                {
                    string msg = reader.GetString();
                    //Debug.Log($"收到广播消息:{msg}");
                    ProtoBase proto = LitJson.JsonMapper.ToObject<ProtoBase>(msg);
                    onReceiveOnSubThread?.Invoke(proto);
                    lock (mainThreadActions)
                    {
                        mainThreadActions.Enqueue(() =>
                        {
                            onReceiveOnMainThread?.Invoke(proto);
                        });
                    }
                }
            };
            receiver = new NetManager(receiverListener);
            receiver.UnconnectedMessagesEnabled = true;
            receiver.BroadcastReceiveEnabled = true;
            receiver.NatPunchEnabled = true;
            receiver.UpdateTime = 15;
            receiver.Start(port);
            Task.Run(async () =>
            {
                isRun = true;
                while (isRun)
                {
                    receiver?.PollEvents();
                    await Task.Delay(15);
                }
            });


            //发送者（不能和接收者共用，因为Start(port)这里不能传参）
            sender = new NetManager(new EventBasedNetListener());
            sender.UnconnectedMessagesEnabled = true;
            sender.NatPunchEnabled = true;
            sender.UpdateTime = 15;
            sender.Start();
            writer = new NetDataWriter();
        }

        private void Update()
        {
            lock (mainThreadActions)
            {
                while (mainThreadActions.Count > 0)
                {
                    mainThreadActions.Dequeue().Invoke();
                }
            }
        }

        public void Send(ProtoBase proto)
        {
            string msg = LitJson.JsonMapper.ToJson(proto);
            //Debug.Log($"发送广播消息：{msg}");
            writer.Reset();
            writer.Put(msg);
            sender.SendBroadcast(writer, port);
            if (proto.targetType == 0)
            {
                //自己也能收到
                Task.Run(() =>
                {
                    onReceiveOnSubThread?.Invoke(proto);
                    lock (mainThreadActions)
                    {
                        mainThreadActions.Enqueue(() =>
                        {
                            onReceiveOnMainThread?.Invoke(proto);
                        });
                    }
                });
            }
        }

        private void OnDestroy()
        {
            isRun = false;
            receiver?.Stop();
            receiver = null;
            sender?.Stop();
            sender = null;
        }

        public static void Destroy()
        {
            if (_instance)
            {
                Destroy(_instance.gameObject);
                _instance = null;
            }
        }
    }
}